{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "mbp_mins.ipynb",
      "provenance": [],
      "authorship_tag": "ABX9TyO+1QhCjcQNn2uiHLApIK9V",
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/vitaldb/examples/blob/master/mbp_mins.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Fvipu9X94deV"
      },
      "source": [
        "# 술 중 혈압에 따른 MINS 발생 위험\n",
        "본 예제에서는 오픈 데이터셋인 vitaldb 를 이용하여 술 중 혈압에 따른 술 후 심근 손상 (myocardial injury after non-cardiac surgery, MINS) 발생 위험을 구해보자."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SqawrQV74y0G"
      },
      "source": [
        "## 필요 라이브러리 및 데이터 다운로드"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "p4HnPH-U4zlv"
      },
      "source": [
        "import pandas as pd\n",
        "import numpy as np\n",
        "\n",
        "df_cases = pd.read_csv(\"https://api.vitaldb.net/cases\")  # 임상 정보\n",
        "df_trks = pd.read_csv('https://api.vitaldb.net/trks')  # 트랙 목록\n",
        "df_labs = pd.read_csv('https://api.vitaldb.net/labs')  # lab 데이터"
      ],
      "execution_count": 1,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SmpjUVoJ48g3"
      },
      "source": [
        "## 본 예제에서 사용할 case 선택\n",
        "vitaldb 환자 중 troponin I 결과가 있는 환자 100명만 사용해보자."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "bC5fzyn_5FnU",
        "outputId": "c33c9e02-6ddc-4132-b186-3fe793178d51",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        }
      },
      "source": [
        "caseids = list(\n",
        "    set(df_trks[df_trks['tname'] == 'Solar8000/ART_MBP']['caseid']) & \n",
        "    set(df_labs[df_labs['name'] == 'Troponin I']['caseid'])\n",
        ")\n",
        "caseids = caseids[:100]\n",
        "print('Total {} cases found'.format(len(caseids)))"
      ],
      "execution_count": 2,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Total 100 cases found\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "i71VaVkr5HDq"
      },
      "source": [
        "## 각 케이스별 데이터 받아오기 및 계산\n",
        "vitaldb 로부터 각 case별 술 후 troponin I 농도를 구해보고 이로부터 MINS 발생 여부를 구해보자. 본 예제에서 MINS 의 정의는 troponin I > 0.028 ng/mL로 정의한다.\n",
        "또한 수술 중 ART_MBP 데이터를 받아와 40-80 mmHg 의 각 threshold 들에 대해 전체 수술 중 해당 threshold  이하에 머무른 측정치의 비율을 구해보자."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "mRz6-gA65WPd",
        "outputId": "69eb4e02-cba6-42c0-a431-18e538f2e6df",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 575
        }
      },
      "source": [
        "# 혈압 기준치\n",
        "mbp_thresholds = np.arange(40, 80)\n",
        "\n",
        "# 최종 결과 저장\n",
        "df = pd.DataFrame()\n",
        "for caseid in caseids:\n",
        "    print('loading {}...'.format(caseid), flush=True, end='')\n",
        "\n",
        "    # 마취 종료 시각을 가져옴\n",
        "    aneend = df_cases[(df_cases['caseid'] == caseid)]['aneend'].values[0]\n",
        "\n",
        "    # 술 후 48 시간 이내 최대 creatinine 농도\n",
        "    postop_tpi = df_labs[(df_labs['caseid'] == caseid) & (df_labs['dt'] > aneend) &\n",
        "        (df_labs['dt'] < aneend + 48 * 3600) & (df_labs['name'] == 'Troponin I')]['result'].max(skipna=True)\n",
        "    if not postop_tpi or np.isnan(postop_tpi):\n",
        "        print('no postop troponin I')\n",
        "        continue\n",
        "\n",
        "    # mins = postop_tpi > 0.028\n",
        "    mins = postop_tpi > 0.028\n",
        "\n",
        "    # 술 중 혈압\n",
        "    tid_mbp = df_trks[(df_trks['caseid'] == caseid) & (df_trks['tname'] == 'Solar8000/ART_MBP')]['tid'].values[0]\n",
        "    mbps = pd.read_csv('https://api.vitaldb.net/' + tid_mbp).values[:,1]\n",
        "    mbps = mbps[~np.isnan(mbps)]\n",
        "    mbps = mbps[(mbps > 20) & (mbps < 150)]\n",
        "    if len(mbps) < 10:\n",
        "        print('no mbp')\n",
        "        continue\n",
        "\n",
        "    # 수술 중 혈압을 1단위로 증가시키면서 해당 시간 동안 머무른 비율을 구함\n",
        "    row = {'mins':mins}\n",
        "    for mbp_threshold in mbp_thresholds:\n",
        "        row['under{}'.format(mbp_threshold)] = np.nanmean(mbps < mbp_threshold) * 100\n",
        "\n",
        "    # 결과 행에 추가\n",
        "    df = df.append(row, ignore_index=True)\n",
        "\n",
        "    print('{}, {}'.format(postop_tpi, 'MINS' if mins else ''))\n",
        "\n",
        "print('{} MINS {:.1f}%'.format(df['mins'].sum(), df['mins'].mean() * 100))"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "loading 1...0.01, \n",
            "loading 4...0.01, \n",
            "loading 6152...0.1, MINS\n",
            "loading 10...0.69, MINS\n",
            "loading 2058...0.01, \n",
            "loading 12...0.07, MINS\n",
            "loading 2060...0.01, \n",
            "loading 4109...no postop troponin I\n",
            "loading 2063...0.02, \n",
            "loading 2064...0.01, \n",
            "loading 17...0.01, \n",
            "loading 6156...0.01, \n",
            "loading 19...no postop troponin I\n",
            "loading 20...0.02, \n",
            "loading 4115...0.03, MINS\n",
            "loading 6159...no postop troponin I\n",
            "loading 6166...0.01, \n",
            "loading 25...0.02, \n",
            "loading 6174...0.01, \n",
            "loading 4128...0.01, \n",
            "loading 2082...0.01, \n",
            "loading 6180...0.01, \n",
            "loading 2085...no postop troponin I\n",
            "loading 4135...0.02, \n",
            "loading 6185...no postop troponin I\n",
            "loading 6186...0.01, \n",
            "loading 4143...0.01, \n",
            "loading 2097...no postop troponin I\n",
            "loading 4146...0.01, \n",
            "loading 2100...0.01, \n",
            "loading 4148..."
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NCz7SN-N5ajv"
      },
      "source": [
        "## 각 혈압 기준치별로 MINS 예측에 대한 odd ratio를 구함\n",
        "case 별 MINS 발생 여부 및 각 threshold 에 머무른 비율을 이용하여 각 혈압 기준치가 MINS 발생 risk 를 얼마나 증가시키는지 odd ratio 를 구해보자."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "kqldpT3h5g9X"
      },
      "source": [
        "# univariate logistic regression을 돌려 odd ratio 를 구함\n",
        "import statsmodels.api as sm\n",
        "df['intercept'] = 1\n",
        "df['mins'] = df['mins'].astype(bool)\n",
        "odd_ratios = []\n",
        "for mbp_threshold in mbp_thresholds:\n",
        "    c = 'under{}'.format(mbp_threshold)\n",
        "    model = sm.Logit(df['mins'], df[['intercept', c]])\n",
        "    res = model.fit()\n",
        "    b = res.params[c]\n",
        "    pval = res.pvalues[c]\n",
        "    odd_ratios.append(np.exp(b))\n",
        "    print('{}\\tb={:.3f}, exp(b)={:.3f}, pval={:.3f}'.format(c, b, np.exp(b), pval))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Wi1dQ0yE5j_V"
      },
      "source": [
        "## 결과를 그림으로\n",
        "대략 MBP가 낮게 머무르는 시간이 늘어날 수록 MINS 의 odd ratio가 증가함을 알 수 있다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "gnTsUGHE5rcY"
      },
      "source": [
        "# 결과를 그림으로\n",
        "import matplotlib.pyplot as plt\n",
        "plt.plot(mbp_thresholds, odd_ratios)\n",
        "plt.show()"
      ],
      "execution_count": null,
      "outputs": []
    }
  ]
}